<?php

namespace app\service;

use app\admin\model\Config;
use EasyWeChat\Pay\Application;
use Exception;
use util\Logger;
use Yansongda\Pay\Pay;


class WxPayService
{
	private static $instance;
	private array  $config;
	private string $appid;
	private string $mchId;

	/**
	 * @throws Exception
	 */
	private function __construct()
	{
		try {
			$appid       = Config::where('name', 'wx_xcx_appid')->value('value');
			$db_config   = Config::where('group', 'wxpay')->column('name, value');
			$this->appid = $appid;
			$this->mchId = $db_config['wxpay_mch_id'];
			$notify_url  = $db_config['wxpay_notify'];
			if (!str_starts_with($notify_url, 'http://') || !str_starts_with($notify_url, 'https://')) {
				$notify_url = request()->domain() . $db_config['wxpay_notify'];
			}
			//把http://替换为https://
			$notify_url   = str_replace('http://', 'https://', $notify_url);
			$config       = [
				'wechat' => [
					'default' => [
						'mch_id'                  => $db_config['wxpay_mch_id'],
						'mch_secret_key'          => $db_config['wxpay_key'],
						'mch_secret_cert'         => app()->getRootPath() . '/cert/wxpay/apiclient_key.pem',
						'mch_public_cert_path'    => app()->getRootPath() . '/cert/wxpay/apiclient_cert.pem',
						'mini_app_id'             => $appid,
						'notify_url'              => $notify_url,
						'wechat_public_cert_path' => [
							"{$db_config['wxpay_id']}" => app()->getRootPath() . '/cert/wxpay/wechatpay.pem',
						],
						'mode'                    => Pay::MODE_NORMAL,
					],
				],
				'logger' => [
					'enable'   => true,
					'file'     => app()->getRootPath() . '/runtime/log/pay/wxpay.log',
					'level'    => 'info', // 建议生产环境等级调整为 info，开发环境为 debug
					'type'     => 'daily', // optional, 可选 daily.
					'max_file' => 10, // optional, 当 type 为 daily 时有效，默认 30 天
				],
				'http'   => [
					'timeout'         => 5.0,
					'connect_timeout' => 5.0,
				],
			];
			$this->config = $config;
		} catch (Exception $e) {
			throw new Exception('获取配置失败');
		}
	}

	public static function getInstance(): self
	{
		if (!self::$instance instanceof self) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * 微信回调
	 *
	 * @return mixed
	 * @throws Exception
	 */
	public function notify()
	{
		try {
			Pay::config($this->config);
			return Pay::wechat()->callback();
		} catch (Exception $e) {
			Logger::error('回调处理失败', [
				'msg'  => $e->getMessage(),
				'line' => $e->getLine(),
				'file' => $e->getFile(),
			]);
			throw new Exception($e->getMessage());
		}
	}

	public function getApp()
	{
		Pay::config($this->config);
		return Pay::wechat();
	}

	/**
	 * 转账到零钱
	 *
	 * @param string $order_no 订单号
	 * @param int    $money    转账金额, 单位分
	 * @param string $openid   微信openid
	 * @param string $name     提现人姓名
	 * @return mixed
	 * @throws Exception
	 */
	public function transfer(string $order_no, int $money, string $openid, string $name, string $remark = '佣金提现')
	{
		try {
			Pay::config($this->config);
			$order = [
				'_action'                     => 'mch_transfer',
				'appid'                       => $this->appid,
				'out_bill_no'                 => $order_no,
				'transfer_scene_id'           => '1000',
				'openid'                      => $openid,
				'transfer_amount'             => $money,
				'transfer_remark'             => $remark,
				'transfer_scene_report_infos' => [
					['info_type' => '活动名称', 'info_content' => $remark],
					['info_type' => '奖励说明', 'info_content' => '用户' . $remark],
				],
			];

			Logger::info('转账-request', [$order]);
			$result          = Pay::wechat()->transfer($order);
			$result          = json_decode($result, true);
			$result['mchId'] = $this->mchId;
			Logger::info('转账-response', [$result]);
			return $result;
		} catch (Exception $e) {
			Logger::error('转账失败', [
				'msg'  => $e->getMessage(),
				'line' => $e->getLine(),
				'file' => $e->getFile(),
			]);
			throw new Exception($e->getMessage());
		}
	}


	/**
	 * 转账到零钱
	 *
	 * @param string $order_no 订单号
	 * @param int    $money    转账金额, 单位分
	 * @param string $openid   微信openid
	 * @param string $name     提现人姓名
	 * @return mixed
	 * @throws Exception
	 */
	public function transferV2(string $order_no, int $money, string $openid, string $name, string $remark = '佣金提现')
	{
		try {
			Pay::config($this->config);
			$order = [
				'_action'                     => 'mch_transfer',
				'appid'                       => $this->appid,
				'out_bill_no'                 => $order_no,
				'transfer_scene_id'           => '1000',
				'openid'                      => $openid,
				'transfer_amount'             => $money,
				'transfer_remark'             => $remark,
				'transfer_scene_report_infos' => [
					['info_type' => '活动名称', 'info_content' => $remark],
					['info_type' => '奖励说明', 'info_content' => '用户' . $remark],
				],
			];

			Logger::info('转账-request', [$order]);
			$result          = Pay::wechat()->transfer($order);
			$result          = json_decode($result, true);
			$result['mchId'] = $this->mchId;
			Logger::info('转账-response', [$result]);
			return $result;
		} catch (Exception $e) {
			Logger::error('转账失败', [
				'msg'  => $e->getMessage(),
				'line' => $e->getLine(),
				'file' => $e->getFile(),
			]);
			throw new Exception($e->getMessage());
		}
	}

	/**
	 * @throws Exception
	 */
	public function queryTransferOrder(string $out_trade_no): array
	{
		// 微信支付配置
		$config = [
			'mch_id'         => $this->mchId,
			'private_key'    => app()->getRootPath() . '/cert/wxpay/apiclient_key.pem', // 商户API私钥
			'certificate'    => app()->getRootPath() . '/cert/wxpay/apiclient_cert.pem', // 商户API证书
			'secret_key'     => $this->config['wechat']['default']['mch_secret_key'],
			'platform_certs' => [
				app()->getRootPath() . '/cert/wxpay/wechatpay.pem',
			],
		];

		// 初始化支付应用
		$app = new Application($config);

		// 请求微信接口
		$resp = $app->getClient()->get(
			"/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{$out_trade_no}"
		);

		// 转成数组
		return $resp->toArray();
	}
}
